package com.managertrade.util.strategy.client;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URI;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import com.esotericsoftware.kryo.NotNull;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.managertrade.util.BackpackApiClientRequest;
import lombok.Data;
import okhttp3.*;
import okhttp3.logging.HttpLoggingInterceptor;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.net.ssl.*;

public class BackpackTradingBot {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    // -------------------- CONFIG --------------------
    public static class TradingConfig {
        public String ticker;
        public String contractId;
        public BigDecimal quantity;
        public BigDecimal takeProfit;
        public BigDecimal tickSize;
        public String direction; // buy or sell
        public BigDecimal maxPosition;
        public int maxOrders;
        public int waitTime; // seconds
        public BigDecimal gridStep; // %
        public BigDecimal stopPrice;
        public BigDecimal pausePrice;
        public boolean asterBoost;


        public String closeOrderSide() {
            return "buy".equals(direction) ? "sell" : "buy";
        }
    }

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class OrderResult {

        // Spot margin fields
        private boolean autoLend;
        private boolean autoLendRedeem;
        private boolean autoBorrow;
        private boolean autoBorrowRepay;

        // Broker and client IDs
        private int brokerId;    // uint16
        private long clientId;   // uint32

        @NotNull
        private OrderType orderType; // Market or Limit

        private boolean postOnly;

        private String price;          // limit order price
        @NotNull
        private String quantity;       // required for most orders
        private String quoteQuantity;  // for market orders

        private boolean reduceOnly;    // futures only

        private SelfTradePrevention selfTradePrevention;

        @NotNull
        private Side side;

        private String stopLossLimitPrice;
        private String stopLossTriggerBy;
        private String stopLossTriggerPrice;

        @NotNull
        private String symbol;

        private String takeProfitLimitPrice;
        private String takeProfitTriggerBy;
        private String takeProfitTriggerPrice;

        private TimeInForce timeInForce;

        private String triggerBy;
        private String triggerPrice;
        private String triggerQuantity;

        private String slippageTolerance;
        private SlippageToleranceType slippageToleranceType;

        // ====== 返回相关字段 ======
        private String id;                  // 订单唯一ID，交易所返回
        private String status;              // 状态，例如 New、Filled、Cancelled
        private String executedQuantity;     // 已成交数量
        private String executedQuoteQuantity; // 已成交报价数量
        private long createdAt;             // 订单创建时间戳

        // ====== 枚举类型 ======
        public enum OrderType { Market, Limit }
        public enum Side { Bid, Ask }
        public enum SelfTradePrevention { RejectTaker, RejectMaker, RejectBoth }
        public enum TimeInForce { GTC, IOC, FOK }
        public enum SlippageToleranceType { TickSize, Percent }
    }


    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class OrderCancelResponse {

        private boolean success;
        private String orderType;
        private String id;
        private Long clientId;
        private Long createdAt;
        private String executedQuantity;
        private String executedQuoteQuantity;
        private String quantity;
        private String quoteQuantity;
        private Boolean reduceOnly;
        private String timeInForce;
        private String selfTradePrevention;
        private String side;
        private String status;
        private String stopLossTriggerPrice;
        private String stopLossLimitPrice;
        private String stopLossTriggerBy;
        private String symbol;
        private String takeProfitTriggerPrice;
        private String takeProfitLimitPrice;
        private String takeProfitTriggerBy;
        private String triggerBy;
        private String triggerPrice;
        private String triggerQuantity;
        private Long triggeredAt;
        private String relatedOrderId;
        private String strategyId;
        private String slippageTolerance;
        private String slippageToleranceType;

        // ✅ 无参构造器 Jackson 需要
        public OrderCancelResponse() {
        }

        // 可选：带 success 的构造器
        public OrderCancelResponse(boolean success) {
            this.success = success;
        }
    }


    /**
     * Backpack 合约持仓返回实体
     */
    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class BackpackPosition {
        private String breakEvenPrice;
        private String cumulativeFundingPayment;
        private String cumulativeInterest;
        private String entryPrice;
        private String estLiquidationPrice;
        private String imf;
        private ImfFunction imfFunction;
        private String markPrice;
        private String mmf;
        private MmfFunction mmfFunction;
        private String netCost;
        private String netExposureNotional;
        private String netExposureQuantity;
        private String netQuantity;
        private String pnlRealized;
        private String pnlUnrealized;
        private String positionId;
        private String subaccountId;
        private String symbol;
        private long userId;

        @Data
        public static class ImfFunction {
            private String base;
            private String factor;
            private String type;
        }

        @Data
        public static class MmfFunction {
            private String base;
            private String factor;
            private String type;
        }
    }
    public static class OrderInfo {
        public String orderId;
        public String side;
        public BigDecimal size;
        public BigDecimal price;
        public String status;
        public BigDecimal filledSize;
        public BigDecimal remainingSize;
    }

    // -------------------- LOGGER --------------------
    public static class TradingLogger {
        public void log(String msg, String level) {
            System.out.println("[" + level + "] " + msg);
        }

        public void logTransaction(String orderId, String side, BigDecimal size, BigDecimal price, String status) {
            System.out.println("[TX] " + orderId + " " + side + " " + size + "@" + price + " " + status);
        }
    }

    // -------------------- WEBSOCKET --------------------
    public static class BackpackWebSocketManager extends WebSocketClient {
        private String publicKey;
        private String secretKey;
        private String symbol;
        private TradingLogger logger;
        private OrderUpdateHandler orderUpdateHandler;
        private AtomicBoolean running = new AtomicBoolean(false);

        private static ObjectMapper mapper = new ObjectMapper();

        public interface OrderUpdateHandler {
            void onOrderUpdate(Map<String, Object> orderData);
        }

        public BackpackWebSocketManager(String wsUrl, String publicKey, String secretKey, String symbol) throws Exception {
            super(new URI(wsUrl));
            this.publicKey = publicKey;
            this.secretKey = secretKey;
            this.symbol = symbol;
        }

        public void setLogger(TradingLogger logger) {
            this.logger = logger;
        }

        public void setOrderUpdateHandler(OrderUpdateHandler handler) {
            this.orderUpdateHandler = handler;
        }

        @Override
        public void onOpen(ServerHandshake handshakedata) {
            running.set(true);
            if (logger != null) logger.log("WebSocket opened for " + symbol, "INFO");
        }

        @Override
        public void onMessage(String message) {
            try {
                Map<String, Object> data = mapper.readValue(message, Map.class);
                String stream = (String) data.getOrDefault("stream", "");
                Map<String, Object> payload = (Map<String, Object>) data.getOrDefault("data", new HashMap<>());
                if (stream.contains("orderUpdate") && orderUpdateHandler != null) {
                    orderUpdateHandler.onOrderUpdate(payload);
                } else {
                    if (logger != null) logger.log("Unknown WS message: " + message, "ERROR");
                }
            } catch (Exception e) {
                if (logger != null) logger.log("WS message parse error: " + e.getMessage(), "ERROR");
            }
        }

        @Override
        public void onClose(int code, String reason, boolean remote) {
            running.set(false);
            if (logger != null) logger.log("WebSocket closed: " + reason, "WARN");
        }

        @Override
        public void onError(Exception ex) {
            if (logger != null) logger.log("WebSocket error: " + ex.getMessage(), "ERROR");
        }
    }

    // -------------------- EXCHANGE CLIENT --------------------
    public static class BackpackClient {
        private String publicKey;
        private PrivateKey privateKey;
        private TradingConfig config;
        private TradingLogger logger;
        private BackpackWebSocketManager wsManager;
        private OkHttpClient httpClient;
        private static ObjectMapper mapper = new ObjectMapper();
        private BackpackWebSocketManager.OrderUpdateHandler orderUpdateHandler;

        private String apiBase = "https://api.backpack.exchange";
        private long window = 5000L; // 默认 5 秒

        public BackpackClient(TradingConfig config, String base64PublicKey, String base64PrivateKey) throws Exception {
            this.config = config;
            this.publicKey = base64PublicKey == null ? null : base64PublicKey.trim();
            try {
                this.privateKey = loadPrivateKey(base64PrivateKey);
            } catch (Exception ex) {
                System.err.println("[ERROR] loadPrivateKey failed: " + ex.getMessage());
                ex.printStackTrace();
                throw ex;
            }
            this.logger = new TradingLogger();

            // 初始化更稳健的 httpClient
            try {
                this.httpClient = createSafeClient();
            } catch (Exception ex) {
                System.err.println("[WARN] createSecureClient failed, falling back to default client: " + ex.getMessage());
                ex.printStackTrace();
                this.httpClient = new OkHttpClient.Builder()
                        .connectTimeout(10, TimeUnit.SECONDS)
                        .readTimeout(15, TimeUnit.SECONDS)
                        .writeTimeout(15, TimeUnit.SECONDS)
                        .retryOnConnectionFailure(true)
                        .build();
            }
        }

        /**
         * loadPrivateKey: 将 raw base64 私钥(32 bytes) 包装为 PKCS#8 (注意：仅当你提供 raw 32-byte 私钥时才可用)
         * 如果你的私钥已经是 PKCS#8，请不要使用此包装。
         */
        private PrivateKey loadPrivateKey(String base64RawKey) throws Exception {
            if (base64RawKey == null) throw new IllegalArgumentException("base64RawKey is null");
            byte[] raw = Base64.getDecoder().decode(base64RawKey.trim());

            // PKCS#8 DER 前缀 (Ed25519) — 这是常见的包装方式（适用于 32 字节私钥）
            byte[] pkcs8Header = new byte[]{
                    0x30, 0x2e,
                    0x02, 0x01, 0x00,
                    0x30, 0x05,
                    0x06, 0x03, 0x2b, 0x65, 0x70,
                    0x04, 0x22,
                    0x04, 0x20
            };

            byte[] pkcs8Bytes = new byte[pkcs8Header.length + raw.length];
            System.arraycopy(pkcs8Header, 0, pkcs8Bytes, 0, pkcs8Header.length);
            System.arraycopy(raw, 0, pkcs8Bytes, pkcs8Header.length, raw.length);

            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkcs8Bytes);
            try {
                return KeyFactory.getInstance("Ed25519", "BC").generatePrivate(spec);
            } catch (InvalidKeySpecException ikse) {
                // 抛出更有用的提示
                System.err.println("[ERROR] InvalidKeySpecException while generating private key: " + ikse.getMessage());
                throw ikse;
            }
        }

        public void setupOrderUpdateHandler(BackpackWebSocketManager.OrderUpdateHandler handler) {
            this.orderUpdateHandler = handler;
        }

        public void connectWebSocket() throws Exception {
            // 注意：Websocket 签名部分若有问题也会导致连接被重置/拒绝
            wsManager = new BackpackWebSocketManager("wss://ws.backpack.exchange", publicKey, Base64.getEncoder().encodeToString(privateKey.getEncoded()), config.contractId);
            wsManager.setLogger(logger);
            wsManager.setOrderUpdateHandler(orderUpdateHandler);
            wsManager.connectBlocking();
        }

        // -------------------- REST API --------------------

        /** 构建按字母排序的 query/body 字符串 */
        private String buildPayload(Map<String, Object> params) {
            if (params == null || params.isEmpty()) return "";
            return params.entrySet().stream()
                    .sorted(Map.Entry.comparingByKey())
                    .map(e -> e.getKey() + "=" + e.getValue())
                    .collect(Collectors.joining("&"));
        }

        private String sign(String message) throws Exception {
            Signature sig = Signature.getInstance("Ed25519", "BC");
            sig.initSign(privateKey);
            sig.update(message.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(sig.sign());
        }

        /**
         * 创建一个更“稳健”的 OkHttpClient，强制 TLSv1.2、HTTP/1.1、并加入基础日志
         */
        public static OkHttpClient createSafeClient() {
            try {
                TrustManager[] trustAllCerts = new TrustManager[]{
                        new X509TrustManager() {
                            public void checkClientTrusted(X509Certificate[] chain, String authType) {}
                            public void checkServerTrusted(X509Certificate[] chain, String authType) {}
                            public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; }
                        }
                };

                SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
                sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
                SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

                HttpLoggingInterceptor logging = new HttpLoggingInterceptor(System.out::println);
                logging.setLevel(HttpLoggingInterceptor.Level.BODY);

                return new OkHttpClient.Builder()
                        .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
                        .hostnameVerifier((hostname, session) -> true)
                        .addInterceptor(logging)
                        .connectTimeout(15, TimeUnit.SECONDS)
                        .readTimeout(15, TimeUnit.SECONDS)
                        .retryOnConnectionFailure(true)
                        .build();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public Map<String, Object> get(String endpoint, Map<String, Object> queryParams) throws Exception {
            // note: for public endpoints (like /api/v1/depth) you can skip signing
            long ts = System.currentTimeMillis();
            String payload = buildPayload(queryParams);
            String signingStr = payload + "&timestamp=" + ts + "&window=" + window;
            String signature = null;
            try {
                signature = sign(signingStr);
            } catch (Exception sx) {
                logger.log("Sign failed: " + sx.getMessage(), "WARN");
            }

            HttpUrl.Builder urlBuilder = HttpUrl.parse(apiBase + endpoint).newBuilder();
            if (queryParams != null) queryParams.forEach((k, v) -> urlBuilder.addQueryParameter(k, String.valueOf(v)));

            Request.Builder rb = new Request.Builder()
                    .url(urlBuilder.build())
                    .get()
                    .addHeader("User-Agent", "Mozilla/5.0 (Java OkHttpClient)")
                    .addHeader("Accept", "application/json")
                    .addHeader("Connection", "close");

            // 如果签名生成成功，则添加签名头（否则保留为空，针对公共接口）
            if (signature != null) {
                rb.addHeader("X-API-KEY", publicKey);
                rb.addHeader("X-SIGNATURE", signature);
                rb.addHeader("X-TIMESTAMP", String.valueOf(ts));
                rb.addHeader("X-WINDOW", String.valueOf(window));
            }

            Request request = rb.build();

            logger.log("GET -> " + request.url().toString(), "DEBUG");
            logger.log("GET headers: " + request.headers().toString(), "DEBUG");

            try (Response response = httpClient.newCall(request).execute()) {
                if (response == null) {
                    logger.log("Null response", "ERROR");
                    return Collections.emptyMap();
                }
                int code = response.code();
                String body = response.body() != null ? response.body().string() : "<empty body>";
                logger.log("HTTP GET response code: " + code, "DEBUG");
                logger.log("HTTP GET response body: " + body, "DEBUG");
                if (!response.isSuccessful()) {
                    throw new RuntimeException("HTTP error: " + code + " body: " + body);
                }
                return mapper.readValue(body, Map.class);
            } catch (SocketException se) {
                logger.log("SocketException on GET: " + se.getMessage(), "ERROR");
                se.printStackTrace();
                throw se;
            } catch (Exception e) {
                logger.log("GET error: " + e.getMessage(), "ERROR");
                e.printStackTrace();
                throw e;
            }
        }

        public Map<String, Object> post(String endpoint, Map<String, Object> body) throws Exception {
            long ts = System.currentTimeMillis();
            String payload = buildPayload(body);
            String signingStr = payload + "&timestamp=" + ts + "&window=" + window;
            String signature = sign(signingStr);

            String json = mapper.writeValueAsString(body);

            Request request = new Request.Builder()
                    .url(apiBase + endpoint)
                    .post(RequestBody.create(json, MediaType.parse("application/json")))
                    .addHeader("User-Agent", "Mozilla/5.0 (Java OkHttpClient)")
                    .addHeader("Accept", "application/json")
                    .addHeader("Connection", "close")
                    .addHeader("X-API-KEY", publicKey)
                    .addHeader("X-SIGNATURE", signature)
                    .addHeader("X-TIMESTAMP", String.valueOf(ts))
                    .addHeader("X-WINDOW", String.valueOf(window))
                    .build();

            logger.log("POST -> " + request.url().toString(), "DEBUG");
            logger.log("POST headers: " + request.headers().toString(), "DEBUG");
            logger.log("POST body: " + json, "DEBUG");

            try (Response response = httpClient.newCall(request).execute()) {
                if (response == null) {
                    logger.log("Null response", "ERROR");
                    return Collections.emptyMap();
                }
                int code = response.code();
                String respText = response.body() != null ? response.body().string() : "<empty body>";
                logger.log("HTTP POST response code: " + code, "DEBUG");
                logger.log("HTTP POST response body: " + respText, "DEBUG");
                if (!response.isSuccessful()) throw new RuntimeException("HTTP error: " + code + " body: " + respText);
                return mapper.readValue(respText, Map.class);
            } catch (SocketException se) {
                logger.log("SocketException on POST: " + se.getMessage(), "ERROR");
                se.printStackTrace();
                throw se;
            } catch (Exception e) {
                logger.log("POST error: " + e.getMessage(), "ERROR");
                e.printStackTrace();
                throw e;
            }
        }



        /**
         * 创建限价单
         * @param quantity
         * @param direction
         * @return
         */
        public OrderResult placeOpenOrder(BigDecimal quantity, String direction) {
            try {
                List<Map<String, Object>> bodyList = new ArrayList<>();
                Map<String, Object> order = new HashMap<>();
                order.put("orderType", "Market");            // 市价开仓
                order.put("symbol", config.contractId);     // 交易对
                if("buy".equalsIgnoreCase(direction)){
                    // 买入（Bid）
                    order.put("side", "Bid");
                }else {
                    order.put("side", "Ask");
                }
                order.put("quantity", quantity);
                bodyList.add(order);
                String respText  = new BackpackApiClientRequest(true).post( bodyList);
                List<BackpackTradingBot.OrderResult> orders = mapper.readValue(respText, new TypeReference<List<OrderResult>>() {});
                return orders.get(0);
            } catch (Exception e) {
                logger.log("Open order error: " + e.getMessage(), "ERROR");
                return null;

            }
        }

        public OrderResult placeCloseOrder(BigDecimal quantity, BigDecimal price, String side) {
            try {
                List<Map<String, Object>> bodyList = new ArrayList<>();
                Map<String, Object> order = new HashMap<>();
                order.put("orderType", "Limit");            // 限价单
                order.put("symbol", config.contractId);     // 交易对
                if("buy".equalsIgnoreCase(side)){
                    order.put("side", "Bid");
                }else {
                    order.put("side", "Ask");
                }
                order.put("price", price.toPlainString());
                order.put("quantity", quantity);
                order.put("timeInForce", "GTC");
                order.put("selfTradePrevention", "RejectTaker");
                bodyList.add(order);
                String respText  = new BackpackApiClientRequest(true).post( bodyList);
                List<BackpackTradingBot.OrderResult> orders = mapper.readValue(respText, new TypeReference<List<OrderResult>>() {});
                return orders.get(0);
            } catch (Exception e) {
                logger.log("Close order error: " + e.getMessage(), "ERROR");
                return null;
            }
        }

        public OrderCancelResponse cancelOrder(BackpackApiClientRequest.CancelOrderRequest request) {
            try {
                String result = new BackpackApiClientRequest(true).cancelOrder(request);
                OrderCancelResponse orderCancelResponse = mapper.readValue(result, OrderCancelResponse.class);
                return orderCancelResponse;
            } catch (Exception e) {
                logger.log("Cancel order error: " + e.getMessage(), "ERROR");
                return null;
            }
        }

        /**
         * 获取某个订单的状态
         * @param
         * @return
         */
        public OrderResult getOrderStatus(String orderId) throws Exception {
            OrderResult orderResult = null;
            String marketType = "PERP";     // 可选：SPOT、PERP 等
            String symbol = config.contractId; // 使用配置symbol
            java.util.List<BackpackTradingBot.OrderResult> openOrders = mapper.readValue(new BackpackApiClientRequest(true).getOpenOrders(marketType, symbol), new TypeReference<List<BackpackTradingBot.OrderResult>>() {}); ;
            if(!CollectionUtils.isEmpty(openOrders)){
                Optional<OrderResult> first = openOrders.stream().filter(k -> Objects.equals(k.getId(), orderId)).findFirst();
                if(first.isPresent()){
                    orderResult = first.get();
                }
            }
            return orderResult;
        }

        public class BBOPrice {
            private BigDecimal bestBid;
            private BigDecimal bestAsk;

            public BBOPrice(BigDecimal bestBid, BigDecimal bestAsk) {
                this.bestBid = bestBid;
                this.bestAsk = bestAsk;
            }

            public BigDecimal getBestBid() { return bestBid; }
            public BigDecimal getBestAsk() { return bestAsk; }

            // 根据方向返回对应价格
            public BigDecimal getPriceByDirection(String direction) {
                return "buy".equalsIgnoreCase(direction) ? bestAsk : bestBid;
            }
        }
        public BBOPrice fetchBBOPrice() {
            try {
                BackpackApiClientRequest client = new BackpackApiClientRequest(true);

                Map<String, String> query = new HashMap<>();
                query.put("symbol", config.contractId);

                String respText = client.get("/depth", query);


                Map<String, Object> depth = mapper.readValue(respText, Map.class);

                List<List<Object>> bids = (List<List<Object>>) depth.get("bids");
                List<List<Object>> asks = (List<List<Object>>) depth.get("asks");

                if (bids == null || bids.isEmpty() || asks == null || asks.isEmpty()) {
                    logger.log("Depth data empty", "ERROR");
                    return new BBOPrice(BigDecimal.ZERO, BigDecimal.ZERO);
                }

                BigDecimal bestBid = bids.stream()
                        .map(b -> new BigDecimal(b.get(0).toString()))
                        .max(Comparator.naturalOrder())
                        .orElse(BigDecimal.ZERO);

                BigDecimal bestAsk = asks.stream()
                        .map(a -> new BigDecimal(a.get(0).toString()))
                        .min(Comparator.naturalOrder())
                        .orElse(BigDecimal.ZERO);

                return new BBOPrice(bestBid, bestAsk);

            } catch (Exception e) {
                logger.log("BBO fetch error: " + e.getMessage(), "ERROR");
                e.printStackTrace();
                return new BBOPrice(BigDecimal.ZERO, BigDecimal.ZERO);
            }
        }


        public java.util.List<BackpackTradingBot.OrderResult> getOpenOrder(String marketType ,String symbol) throws Exception {
            BackpackApiClientRequest client = new BackpackApiClientRequest(true);
            String openOrders = client.getOpenOrders(marketType, symbol);
            java.util.List<BackpackTradingBot.OrderResult> openOrdersResult = mapper.readValue(openOrders, new TypeReference<List<BackpackTradingBot.OrderResult>>() {});
            return openOrdersResult;
        }


        public BackpackTradingBot.BackpackPosition getCurrentPosition() throws Exception {
            BackpackApiClientRequest client = new BackpackApiClientRequest(true);
            String openOrders = client.getCurrentPosition(config.contractId);
            if(!StringUtils.isEmpty(openOrders)){
                List<BackpackTradingBot.BackpackPosition> positions = mapper.readValue(openOrders, new TypeReference<List<BackpackTradingBot.BackpackPosition>>() {});
                if(!CollectionUtils.isEmpty(positions)){
                    return positions.get(0);
                }
            }
            return null;
        }

        public void disconnect() {
            try {
                if (wsManager != null) wsManager.closeBlocking();
            } catch (Exception e) {
                logger.log("Error closing WS: " + e.getMessage(), "ERROR");
            }
        }
    }

    // -------------------- TRADING BOT --------------------
    public static class TradingBot {
        // 用来记录当前挂着的平仓订单
        // 全局线程安全列表
        // 上一次开仓订单时间戳（毫秒）
        private long lastOpenOrderTime = 0;

        private TradingConfig config;
        private TradingLogger logger;
        private BackpackClient client;
        private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        private AtomicBoolean shutdown = new AtomicBoolean(false);

        public TradingBot(TradingConfig config, String publicKey, String privateKey) throws Exception {
            this.config = config;
            this.logger = new TradingLogger();
            this.client = new BackpackClient(config, publicKey, privateKey);

            client.setupOrderUpdateHandler(orderData -> {
                String status = (String) orderData.get("status");
                if ("FILLED".equalsIgnoreCase(status) || "Filled".equalsIgnoreCase(status)) {
                    logger.logTransaction((String) orderData.get("order_id"),
                            (String) orderData.get("side"),
                            new BigDecimal(orderData.get("filled_size").toString()),
                            new BigDecimal(orderData.get("price").toString()), status);
                }
            });
        }

        public void run() throws Exception {
            client.connectWebSocket();
            // 定时触发 tradeLoop
            scheduler.scheduleAtFixedRate(this::tradeLoop, 0, config.waitTime, TimeUnit.SECONDS);
        }

        // 当前本地网格挂单记录
        private final List<GridUnit> gridUnits = new CopyOnWriteArrayList<>();

        private BigDecimal lastFunds = BigDecimal.ZERO;
        // 上一次方向
        private String lastDirection = null;
        // 网格挂单记录
        @Data
        class GridUnit {
            BigDecimal openPrice;
            BigDecimal closePrice;
            BigDecimal quantity;
            OrderResult openOrder;
            OrderResult closeOrder;

            GridUnit(BigDecimal openPrice, BigDecimal closePrice, BigDecimal quantity) {
                this.openPrice = openPrice;
                this.closePrice = closePrice;
                this.quantity = quantity;
            }
        }

        private void tradeLoop() {
            if (shutdown.get()) return;

            try {
                // 1️⃣ 获取账户余额
                Map<String, BackpackApiClientRequest.WalletBalance> balances = new BackpackApiClientRequest(true).getAccountFunds();
                BigDecimal usdc = balances.getOrDefault("USDC", new BackpackApiClientRequest.WalletBalance()).getAvailable();
                BigDecimal usdt = balances.getOrDefault("USDT", new BackpackApiClientRequest.WalletBalance()).getAvailable();
                BigDecimal totalFunds = usdc.add(usdt);
                if(totalFunds.compareTo(BigDecimal.ZERO)==0){
                    logger.log("获取资产数据异常:系统时间 " + System.currentTimeMillis(), "INFO");
                    return;
                }

                // 2️⃣ 判断资金变化方向
                String newDirection;
                if (lastFunds.compareTo(BigDecimal.ZERO) > 0) {
                    int cmp = totalFunds.compareTo(lastFunds);
                    if (cmp > 0) {
                        // 总资产上涨 → 保持原方向
                        newDirection = lastDirection != null ? lastDirection : config.direction;
                        logger.log("总资产上涨 → 保持方向: " + newDirection, "INFO");
                    } else if (cmp < 0) {
                        // 总资产下降 → 反向
                        newDirection = lastDirection != null
                                ? ("buy".equalsIgnoreCase(lastDirection) ? "sell" : "buy")
                                : config.direction;
                        logger.log("总资产下降 → 反向方向: " + newDirection, "INFO");
                    } else {
                        logger.log("总资产未变化，保持上次方向 cmp :"+cmp + totalFunds + lastFunds, "INFO");
                        // 5️⃣ 更新状态
                        lastFunds = totalFunds;
                        return;
                    }
                } else {
                    // 初始无资金记录 → 默认方向
                    newDirection = config.direction;
                    logger.log("初始方向: " + newDirection, "INFO");
                }
                logger.log("当前总资产:"+ totalFunds+ "上一次总资金:"+lastFunds+"当前方向"+newDirection,"INFO");
                // 3️⃣ 查询当前持仓
                BackpackPosition positionInfo = client.getCurrentPosition();
                BigDecimal currentPosition = positionInfo != null
                        ? new BigDecimal(positionInfo.getNetQuantity())
                        : BigDecimal.ZERO;

                // 4️⃣ 判断操作逻辑
                if (positionInfo == null) {
                    // 无持仓 → 开仓
                    OrderResult order = client.placeOpenOrder(config.quantity, newDirection);
                    if (order != null) {
                        logger.logTransaction(order.getId(), newDirection, config.quantity, BigDecimal.ZERO, "Open");
                        logger.log("开仓: " + config.quantity + " → " + newDirection, "INFO");
                    }
                } else if ((currentPosition.compareTo(BigDecimal.ZERO) > 0 && "sell".equalsIgnoreCase(newDirection)) ||
                        (currentPosition.compareTo(BigDecimal.ZERO) < 0 && "buy".equalsIgnoreCase(newDirection))) {
                    // 持仓方向与资金变化方向相反 → 平仓再开仓
                    BigDecimal closeQty = currentPosition.abs();
                    String closeDirection = currentPosition.compareTo(BigDecimal.ZERO) > 0 ? "sell" : "buy";
                    OrderResult closeOrder = client.placeOpenOrder(closeQty, closeDirection);
                    if (closeOrder != null) {
                        logger.logTransaction(closeOrder.getId(), closeDirection, closeQty, BigDecimal.ZERO, "Close");
                        logger.log("平仓完成: " + closeQty + " → " + closeDirection, "INFO");
                    }

                    // 再开新仓
                    OrderResult newOrder = client.placeOpenOrder(config.quantity, newDirection);
                    if (newOrder != null) {
                        logger.logTransaction(newOrder.getId(), newDirection, config.quantity, BigDecimal.ZERO, "Open");
                        logger.log("反向开仓: " + config.quantity + " → " + newDirection, "INFO");
                    }
                } else {
                    // 持仓方向与资金变化方向一致 → 不操作
                    logger.log("方向一致，无需操作，持仓: " + currentPosition, "INFO");
                }

                // 5️⃣ 更新状态
                lastFunds = totalFunds;
                lastDirection = newDirection;

            } catch (Exception e) {
                logger.log("Trade loop error: " + e.getMessage(), "ERROR");
                e.printStackTrace();
            }
        }


        /**
         * 同步持仓与平仓挂单，保证持仓和对应的平仓挂单一致：
         * - 若持仓量大于现有未成交平仓挂单量，创建差额的平仓挂单
         * - 若持仓量小于现有未成交平仓挂单量，取消多余的平仓挂单
         */
        private void syncPositionAndCloseOrders(BigDecimal currentPosition) {
            try {
                // 统计本地未成交的平仓挂单总量
                BigDecimal totalClosePending = gridUnits.stream()
                        .filter(u -> u.closeOrder != null && !"Filled".equalsIgnoreCase(u.closeOrder.getStatus()))
                        .map(u -> new BigDecimal(u.closeOrder.getQuantity()))
                        .reduce(BigDecimal.ZERO, BigDecimal::add);

                if (currentPosition.compareTo(totalClosePending) > 0) {
                    // 需要新增平仓挂单
                    BigDecimal diff = currentPosition.subtract(totalClosePending);
                    logger.log("Position larger than pending close orders, need to place close for: " + diff, "INFO");

                    // 计算平仓价（基于当前市价和 takeProfit）
                    BackpackClient.BBOPrice bbo = client.fetchBBOPrice();
                    BigDecimal marketPrice = "buy".equalsIgnoreCase(config.direction) ? bbo.getBestAsk() : bbo.getBestBid();
                    BigDecimal closePrice = "buy".equalsIgnoreCase(config.direction)
                            ? marketPrice.multiply(BigDecimal.ONE.add(config.takeProfit.divide(BigDecimal.valueOf(100), 8, RoundingMode.HALF_UP)))
                            : marketPrice.multiply(BigDecimal.ONE.subtract(config.takeProfit.divide(BigDecimal.valueOf(100), 8, RoundingMode.HALF_UP)));
                    BigDecimal adjusted = adjustPriceByTick(closePrice, config.closeOrderSide(), config.tickSize);

                    // 下单直到差额为0或达到 maxOrders 限制
                    while (diff.compareTo(BigDecimal.ZERO) > 0) {
                        BigDecimal qty = diff.compareTo(config.quantity) >= 0 ? config.quantity : diff;
                        OrderResult close = client.placeCloseOrder(qty, adjusted, config.closeOrderSide());
                        if (close != null) {
                            GridUnit newUnit = new GridUnit(BigDecimal.ZERO, adjusted, qty);
                            newUnit.closeOrder = close;
                            gridUnits.add(newUnit);
                            logger.logTransaction(close.getId(), config.closeOrderSide(), qty, adjusted, "SyncCloseAdd");
                            diff = diff.subtract(qty);
                        } else {
                            // 下单失败则退出以避免无限循环
                            break;
                        }

                        // 避免创建过多订单
                        long activeCloseCount = gridUnits.stream().filter(u -> u.closeOrder != null && !"Filled".equalsIgnoreCase(u.closeOrder.getStatus())).count();
                        if (config.maxOrders > 0 && activeCloseCount >= config.maxOrders) break;
                    }
                } else if (currentPosition.compareTo(totalClosePending) < 0) {
                    // 需要取消多余的平仓挂单
                    BigDecimal excess = totalClosePending.subtract(currentPosition);
                    logger.log("Pending close orders exceed position by: " + excess + ", will cancel extras", "INFO");

                    for (GridUnit u : gridUnits) {
                        if (excess.compareTo(BigDecimal.ZERO) <= 0) break;
                        if (u.closeOrder != null && !"Filled".equalsIgnoreCase(u.closeOrder.getStatus())) {
                            BigDecimal q = new BigDecimal(u.closeOrder.getQuantity());
                            BackpackApiClientRequest.CancelOrderRequest cancel = new BackpackApiClientRequest.CancelOrderRequest();
                            cancel.setOrderId(u.closeOrder.getId());
                            client.cancelOrder(cancel);
                            logger.log("Cancelled extra close order: " + u.closeOrder.getId(), "INFO");
                            excess = excess.subtract(q);
                            u.closeOrder = null;
                        }
                    }
                }

            } catch (Exception e) {
                logger.log("syncPositionAndCloseOrders error: " + e.getMessage(), "ERROR");
            }
        }

        /**
         * 持仓变化时动态调整网格挂单数量或价格
         */
        private void adjustGridOrders(BigDecimal currentPosition) {
            for (GridUnit unit : gridUnits) {
                try {
                    // 仅调整未成交开仓挂单
                    if (unit.openOrder != null
                            && !"Filled".equalsIgnoreCase(unit.openOrder.getStatus())) {

                        BigDecimal desiredQty = calculateDesiredQuantity(currentPosition);
                        if (desiredQty.compareTo(new BigDecimal(unit.openOrder.getQuantity())) != 0) {
                            // 取消旧挂单
                            BackpackApiClientRequest.CancelOrderRequest cancelOrderRequest = new BackpackApiClientRequest.CancelOrderRequest();
                            cancelOrderRequest.setOrderId(unit.openOrder.getId());
                            cancelOrderRequest.setSymbol(config.contractId);
                            client.cancelOrder(cancelOrderRequest);
                            // 重新下单
                            OrderResult newOrder = client.placeOpenOrder(desiredQty, config.direction);
                            if (newOrder != null) unit.openOrder = newOrder;
                            logger.log("Adjusted open order: " + unit.openOrder.getId() + ", qty=" + desiredQty, "INFO");
                        }
                    }

                    // 动态调整平仓价格（可选）
                    if (unit.closeOrder != null
                            && !"Filled".equalsIgnoreCase(unit.closeOrder.getStatus())) {

                        BigDecimal newClosePrice = calculateTargetClosePrice(unit);
                        if (newClosePrice.compareTo(unit.closePrice) != 0) {
                            BackpackApiClientRequest.CancelOrderRequest cancelOrderRequest = new BackpackApiClientRequest.CancelOrderRequest();
                            cancelOrderRequest.setOrderId(unit.closeOrder.getId());
                            cancelOrderRequest.setSymbol(unit.closeOrder.getSymbol());
                            client.cancelOrder(cancelOrderRequest);
                            OrderResult newCloseOrder = client.placeCloseOrder(unit.quantity, newClosePrice, config.closeOrderSide());
                            if (newCloseOrder != null) {
                                unit.closeOrder = newCloseOrder;
                                unit.closePrice = newClosePrice;
                                logger.log("Adjusted close order: " + unit.closeOrder.getId()
                                        + ", price=" + newClosePrice, "INFO");
                            }
                        }
                    }

                } catch (Exception e) {
                    logger.log("adjustGridOrders error: " + e.getMessage(), "ERROR");
                }
            }
        }

        /**
         * 计算动态开仓数量，可根据当前持仓调整
         */
        private BigDecimal calculateDesiredQuantity(BigDecimal currentPosition) {
            BigDecimal remainingQty = config.maxPosition.subtract(currentPosition);
            return remainingQty.compareTo(config.quantity) > 0 ? config.quantity : remainingQty.max(BigDecimal.ZERO);
        }

        /**
         * 计算动态平仓价格，可根据策略调整
         */
        /**
         * 计算动态平仓价格，可根据策略调整，并对齐最小价格步长（tick size）
         */
        private BigDecimal calculateTargetClosePrice(GridUnit unit) {
            try {
                // 获取开仓价格
                BigDecimal filledPrice = unit.openOrder != null
                        && unit.openOrder.getPrice() != null
                        && !unit.openOrder.getPrice().isEmpty()
                        ? new BigDecimal(unit.openOrder.getPrice())
                        : BigDecimal.ZERO;

                if (filledPrice.compareTo(BigDecimal.ZERO) <= 0) {
                    // fallback to market
                    BackpackClient.BBOPrice bbo = client.fetchBBOPrice();
                    filledPrice = "buy".equalsIgnoreCase(config.direction) ? bbo.getBestAsk() : bbo.getBestBid();
                }

                // 计算目标平仓价格
                BigDecimal targetPrice;
                BigDecimal takeProfitPercent = config.takeProfit.divide(BigDecimal.valueOf(100), 8, RoundingMode.HALF_UP);
                if ("buy".equalsIgnoreCase(config.direction)) {
                    targetPrice = filledPrice.multiply(BigDecimal.ONE.add(takeProfitPercent));
                } else {
                    targetPrice = filledPrice.multiply(BigDecimal.ONE.subtract(takeProfitPercent));
                }

                // 对齐价格到最小 tick
                BigDecimal tickSize = config.tickSize != null ? config.tickSize : BigDecimal.valueOf(0.01); // 默认 0.01
                targetPrice = targetPrice.divide(tickSize, 0, RoundingMode.HALF_UP).multiply(tickSize);

                // 确保目标价格和开仓价格至少有一个 tick 差距
                if (targetPrice.compareTo(filledPrice) == 0) {
                    targetPrice = "buy".equalsIgnoreCase(config.direction)
                            ? targetPrice.add(tickSize)
                            : targetPrice.subtract(tickSize);
                }

                return targetPrice;
            } catch (Exception e) {
                logger.log("calculateTargetClosePrice error: " + e.getMessage(), "ERROR");
                return BigDecimal.ZERO;
            }
        }


        /**
         * 同步交易所挂单状态，更新本地 gridUnits
         */
        private void updateActiveOrders() {
            try {
                List<OrderResult> exchangeOrders = client.getOpenOrder("PERP", config.contractId);

                if (exchangeOrders == null || exchangeOrders.size() ==0) return;

                // 同步开仓挂单（方向按 config.direction）
                List<OrderResult> exchangeActiveOpen = exchangeOrders.stream()
                        .filter(o -> o.getSide() != null && (("buy".equalsIgnoreCase(config.direction) && o.getSide().toString().equals("Bid")) || ("sell".equalsIgnoreCase(config.direction) && o.getSide().toString().equals("Ask"))))
                        .filter(o -> o.getStatus() != null && (o.getStatus().equalsIgnoreCase("New")
                                || o.getStatus().equalsIgnoreCase("PartiallyFilled") || o.getStatus().equalsIgnoreCase("Filled")))
                        .collect(Collectors.toList());

                // 同步平仓挂单（反方向）
                List<OrderResult> exchangeActiveClose = exchangeOrders.stream()
                        .filter(o -> o.getSide() != null && (("buy".equalsIgnoreCase(config.direction) && o.getSide().toString().equals("Ask")) || ("sell".equalsIgnoreCase(config.direction) && o.getSide().toString().equals("Bid"))))
                        .filter(o -> o.getStatus() != null && (o.getStatus().equalsIgnoreCase("New")
                                || o.getStatus().equalsIgnoreCase("PartiallyFilled") || o.getStatus().equalsIgnoreCase("Filled")))
                        .collect(Collectors.toList());

                for (GridUnit unit : gridUnits) {
                    // 更新开仓状态
                    exchangeActiveOpen.stream()
                            .filter(o -> unit.openOrder != null && o.getId().equals(unit.openOrder.getId()))
                            .findFirst()
                            .ifPresent(unit::setOpenOrder);

                    // 更新平仓状态
                    exchangeActiveClose.stream()
                            .filter(o -> unit.closeOrder != null && o.getId().equals(unit.closeOrder.getId()))
                            .findFirst()
                            .ifPresent(unit::setCloseOrder);
                }

                // 清理已完成网格
                gridUnits.removeIf(u -> u.openOrder != null && "Filled".equalsIgnoreCase(u.openOrder.getStatus())
                        && u.closeOrder != null && "Filled".equalsIgnoreCase(u.closeOrder.getStatus()));

            } catch (Exception e) {
                logger.log("updateActiveOrders error: " + e.getMessage(), "ERROR");
                e.printStackTrace();
            }
        }

        /**
         * 开仓成交处理 → 下平仓单
         */
        private void handleFilledOpen(GridUnit unit) {
            try {
                BigDecimal executedQty = unit.openOrder.getExecutedQuantity() != null && !unit.openOrder.getExecutedQuantity().isEmpty()
                        ? new BigDecimal(unit.openOrder.getExecutedQuantity())
                        : BigDecimal.ZERO;
                BigDecimal executedQuoteQty = unit.openOrder.getExecutedQuoteQuantity() != null && !unit.openOrder.getExecutedQuoteQuantity().isEmpty()
                        ? new BigDecimal(unit.openOrder.getExecutedQuoteQuantity())
                        : BigDecimal.ZERO;
                if (executedQty.compareTo(BigDecimal.ZERO) <= 0) return;

                BigDecimal filledPrice = executedQuoteQty.compareTo(BigDecimal.ZERO) > 0
                        ? executedQuoteQty.divide(executedQty, 8, RoundingMode.HALF_UP)
                        : (unit.openOrder.getPrice() != null ? new BigDecimal(unit.openOrder.getPrice()) : BigDecimal.ZERO);

                BigDecimal closePrice = "buy".equalsIgnoreCase(config.direction)
                        ? filledPrice.multiply(BigDecimal.ONE.add(config.takeProfit.divide(BigDecimal.valueOf(100), 8, RoundingMode.HALF_UP)))
                        : filledPrice.multiply(BigDecimal.ONE.subtract(config.takeProfit.divide(BigDecimal.valueOf(100), 8, RoundingMode.HALF_UP)));

                BigDecimal adjustedPrice = adjustPriceByTick(closePrice, config.closeOrderSide(), config.tickSize);

                // 去重平仓单
                Set<BigDecimal> existingClosePrices = gridUnits.stream()
                        .filter(u -> u.closeOrder != null)
                        .map(u -> u.closePrice)
                        .collect(Collectors.toSet());

                if (!existingClosePrices.contains(adjustedPrice)) {
                    OrderResult closeOrder = client.placeCloseOrder(executedQty, adjustedPrice, config.closeOrderSide());
                    if (closeOrder != null) {
                        unit.closeOrder = closeOrder;
                        unit.closePrice = adjustedPrice;
                        logger.logTransaction(closeOrder.getId(), config.closeOrderSide(), executedQty, adjustedPrice, "Close (from filled open)");
                    }
                }

            } catch (Exception e) {
                logger.log("handleFilledOpen error: " + e.getMessage(), "ERROR");
                e.printStackTrace();
            }
        }



        /**
         * 根据 tickSize 调整价格
         * @param targetPrice 目标价格
         * @param side "buy" 或 "sell"
         * @param tickSize 市价最小变动单位
         * @return 调整后的价格
         */
        public BigDecimal adjustPriceByTick(BigDecimal targetPrice, String side, BigDecimal tickSize) {
            if (targetPrice == null) return BigDecimal.ZERO;
            if ("buy".equalsIgnoreCase(side)) {
                // 买单 → 向下取整
                return targetPrice
                        .divide(tickSize, 0, RoundingMode.DOWN)
                        .multiply(tickSize);
            } else if ("sell".equalsIgnoreCase(side) || "ask".equalsIgnoreCase(side)) {
                // 卖单 → 向上取整
                return targetPrice
                        .divide(tickSize, 0, RoundingMode.UP)
                        .multiply(tickSize);
            } else {
                // 默认向上
                return targetPrice
                        .divide(tickSize, 0, RoundingMode.UP)
                        .multiply(tickSize);
            }
        }

        public void stop() {
            shutdown.set(true);
            scheduler.shutdown();
            client.disconnect();
        }
    }

    // -------------------- MAIN --------------------
    public static void main(String[] args) throws Exception {
        TradingConfig config = new TradingConfig();
        config.ticker = "BTC/USDT";
        //系统里是 最小价格变动单位
        config.tickSize = BigDecimal.valueOf(0.01);
        config.contractId = "ASTER_USDC_PERP";
        config.quantity = BigDecimal.valueOf(5);
        config.takeProfit = BigDecimal.valueOf(0.1);
        config.gridStep = BigDecimal.valueOf(0.3);
        config.waitTime = 60;
        config.direction = "buy";
        config.maxPosition= BigDecimal.valueOf(100);
        config.maxOrders = 10;
        String publicKey = "/t6YQSuvitAnUQFs8U9e2EzGSacF+zuk62xax0f0sFs=";
        String privateKey = "PNkR35Tdr24gNAUt6+x6MsiBsltQqdx58a4wv+nzQqw=";

        TradingBot bot = new TradingBot(config, publicKey, privateKey);
        bot.run();

        Runtime.getRuntime().addShutdownHook(new Thread(bot::stop));
    }
}
